/**
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 *   
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 */
package org.jiopi.ibean.kernel.context;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Stack;
import java.util.List;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.jiopi.framework.ControlPanel;
import org.jiopi.framework.ModuleConsole;
import org.jiopi.framework.core.JiopiConfigConstants;
import org.jiopi.framework.annotation.module.InstanceType;
import org.jiopi.ibean.kernel.NameVersion;
import org.jiopi.ibean.kernel.Version;
import org.jiopi.ibean.kernel.config.BlueprintAnnotations;
import org.jiopi.ibean.kernel.config.JIOPiConfig;
import org.jiopi.ibean.kernel.config.ModuleAnnotations;
import org.jiopi.ibean.kernel.context.classloader.IBeanClassLoader;
import org.jiopi.ibean.kernel.util.ClassUtil;
import org.jiopi.ibean.kernel.util.ObjectAccessor;
import org.jiopi.ibean.kernel.util.XMLMerger;
import org.jiopi.ibean.kernel.context.classloader.JIOPiJarClassLoader;

/**
 * 
 * 一个Module实例的相关操作
 * 
 * 一个iBeanContext下, ModuleName+version 确定同一个Module运行环境(ClassLoader)
 * 
 * ModuleConsole 的用途是将各种非标准化的JIOPi编程规范 转换为标准的Java调用规范
 * 
 * @version 0.1 2010.4.19
 * @since iBeanKernel0.1 2010.4.19
 *
 */
public class ModuleConsoleImpl implements ModuleConsole{
	
	private static Logger logger = Logger.getLogger(ModuleConsoleImpl.class);
	
	private final ContextCentralConsoleKernel ccck;
	
	private final NameVersion moduleVersion;
	
	/**
	 * 当前Module使用的ClassLoader
	 */
	public final IBeanClassLoader moduleClassLoader;
	
	public final static ThreadLocal<ClassLoader> originContextClassLoader = new ThreadLocal<ClassLoader>();
	
	/**
	 * caller IBeanClassLoader stack
	 */
	private final static ThreadLocal<LinkedHashSet<IBeanClassLoader>> originIBeanClassLoaderStack = new ThreadLocal<LinkedHashSet<IBeanClassLoader>>();
	
	private final static ThreadLocal<Stack<IBeanClassLoader>> assemblingStack = new ThreadLocal<Stack<IBeanClassLoader>>();
	
	private final ObjectPool objectPool = new ObjectPool();
	
	
	
	public ModuleConsoleImpl(NameVersion moduleVersion,IBeanClassLoader moduleClassLoader,ContextCentralConsoleKernel ccck){
		this.moduleClassLoader = moduleClassLoader;
		this.ccck = ccck;
		this.moduleVersion = moduleVersion;
		
		if(logger.isDebugEnabled())logger.debug("ModuleConsoleImpl init with ClassLoader "+moduleClassLoader);
	}
	
	
	private static void clearOriginContextClassLoader(){
		originContextClassLoader.set(null);
	}
	private static boolean initOriginContextClassLoader(){
		ClassLoader o = originContextClassLoader.get();
		boolean needClean = false;
		if(o==null){
			needClean = true;
			o = Thread.currentThread().getContextClassLoader();
			originContextClassLoader.set(o);
			//count call stack of JIOPiJarClassLoader
			Class<?>[] callClasses = ClassUtil.getClassContext();
			LinkedHashSet<IBeanClassLoader> classLoaderStack = new LinkedHashSet<IBeanClassLoader>();
			for(Class<?> c:callClasses){
				ClassLoader cl = c.getClassLoader();
				if(cl instanceof JIOPiJarClassLoader){
					IBeanClassLoader icl = ((JIOPiJarClassLoader)cl).parentClassLoader;
					if(classLoaderStack.contains(icl)){
						classLoaderStack.remove(icl);
					}
					classLoaderStack.add(icl);
				}
			}
			originIBeanClassLoaderStack.set(classLoaderStack);
		}
		return needClean;
	}
	
	private static Stack<IBeanClassLoader> getAssemblingStack(){
		Stack<IBeanClassLoader> s = assemblingStack.get();
		if(s == null){
			s = new Stack<IBeanClassLoader>();
			assemblingStack.set(s);
		}
		return s;
	}
	
	/**
	 * assemble the given Object
	 */
	private void autoAssembling(JIOPiConfig.Controlpanel assemblingConfig,Object obj){
		if(moduleClassLoader==null)return;
		if(obj instanceof ControlPanelImpl){
			ControlPanelImpl cpi = (ControlPanelImpl)obj;
			obj = cpi.wrapped;
		}
		
		try{
			Class<?> objClass = obj.getClass();
			if(objClass == Class.class )objClass = (Class<?>)obj;
			HashMap<String,Object> accessoryPool = new HashMap<String,Object>();
			accessoryPool.put("this", obj);
			
			//set properties
			if(assemblingConfig!=null&&assemblingConfig.properties.size()>0){
				for(String key:assemblingConfig.properties.keySet()){
					String value = assemblingConfig.properties.get(key);
					try{//catch exception because when autoAssembling Class,and set is not static
						Class<?> varClass = ObjectAccessor.Accessible.variableClass(obj, key);
						Object newValue = ConvertUtils.convert(value,varClass);
						ObjectAccessor.Accessible.setVariable(obj, key, newValue);
					}catch(Exception e){
					}
				}
			}
			
			//socket accessories to this object
			if(assemblingConfig!=null) socketAccessories(accessoryPool,"this",assemblingConfig);
			
			//System.out.println("\n===autoAssembling"+objClass.getName()+" with assemblingConfig:"+(assemblingConfig!=null)+"===");
			ModuleAnnotations.ControlPanel controlPanelConfig = moduleClassLoader.getControlPaneConfig(objClass.getName());
			
			if(controlPanelConfig!=null){
				//socket-module
				if(controlPanelConfig.socketModule.size()>0){
					for(String varName:controlPanelConfig.socketModule.keySet()){
						HashMap<String,String> socketModuleConfig = controlPanelConfig.socketModule.get(varName);
						socketInnerAccessory(obj,varName,socketModuleConfig,assemblingConfig,accessoryPool);
					}
				}
				
				//socket control panel
				if(controlPanelConfig.socketControlpanel.size()>0){
					for(String varName:controlPanelConfig.socketControlpanel.keySet()){
						HashMap<String,String> socketControlpanelConfig = controlPanelConfig.socketControlpanel.get(varName);
						socketInnerAccessory(obj,varName,socketControlpanelConfig,assemblingConfig,accessoryPool);
					}
				}
				//System.out.println("\n===autoAssembling"+objClass.getName()+" over===\n");
			}
		}catch(Exception e){
			logger.warn("Assembling object error : "+obj+" "+e.getMessage());
		}
	}
	
	private void socketInnerAccessory(Object obj,String varName,HashMap<String,String> socketModuleConfig,JIOPiConfig.Controlpanel assemblingConfig,HashMap<String,Object> accessoryPool){
		String id = socketModuleConfig.get("id");
		String module = socketModuleConfig.get("module");
		String version = socketModuleConfig.get("version");
		String configuration = socketModuleConfig.get("configuration");
		if(module!=null||id!=null){
			//System.out.println(" socketModule:"+varName+"[id="+id+";module="+module+";version="+version+";configuration="+configuration+"]");
			//new Dependency module Instance
			//replace Annotation configuration
			if(assemblingConfig!=null&&id!=null){
				HashMap<String,String> annotationConfig = assemblingConfig.innerAccessories.get(id);
				if(annotationConfig!=null){
					String a_module = annotationConfig.get("module");
					String a_version = annotationConfig.get("version");
					String a_configuration = annotationConfig.get("configuration");
					if(a_module!=null) module = a_module;
					if(a_version!=null) version = a_version;
					if(a_configuration!=null) configuration = a_configuration;
					//System.out.println("reset socketModule:"+varName+"[id="+id+";module="+module+";version="+version+";configuration="+configuration+"]");
				}
			}
			if("this".equals(module)){
				module = moduleVersion.name;
				version = moduleVersion.version;
			}

			//System.out.println(obj.getClass() +" next reset socketModule:"+varName+"[id="+id+";module="+module+";version="+version+";configuration="+configuration+"]");
			boolean isControlPanel = false;
			
			//1.get var interface type and version
			Class<?> varClass = null;
			try{
				varClass = ObjectAccessor.Accessible.variableClass(obj, varName);
			}catch(Exception e){
				return;
			}
			//System.out.println("var type : "+varClass.getName());
			
			String cpName = varClass.getName();
			
			if(varClass == org.jiopi.framework.ControlPanel.class){
				isControlPanel = true;
				cpName = socketModuleConfig.get("controlpanel");
				//System.out.println(">>--->>>socket isControlPanel "+cpName);
			}
			
			//2.get version of interface class
			Version useVersion = null;
			if(isControlPanel){
				useVersion = new Version(version);
			}else{
				Version blueprintVersion = moduleClassLoader.getBlueprintVersion(varClass);
				if(blueprintVersion == null){
					 //if can't get version, interface may be a common type,use latest version for default
					blueprintVersion = new Version(null);
				}
				//System.out.println("blueprint version is "+blueprintVersion.version);
				//3.compare version
				useVersion = blueprintVersion;
				if(version!=null){
					Version definedVersion = new Version(version);
					if(blueprintVersion.isCompatible(definedVersion)){
						useVersion = definedVersion;
					}
				}
			}
			//System.out.println("use version is "+useVersion.version);
			
			//4. instance module
			
			Object innerAccessory = newControlPanel(module, useVersion.version,cpName,configuration, null);
			//System.out.println("socket a module : "+innerAccessory.getClass());
			if(id!=null){
				accessoryPool.put(id, innerAccessory);
			}
			//5. Assembling by configuration file to this inner-accessory
			if(assemblingConfig!=null && id!=null){
				//5.1 find connecting accessories
				socketAccessories(accessoryPool,id,assemblingConfig);
			}
			Object innerControlPanel = innerAccessory;
			if(isControlPanel){
				innerControlPanel = ObjectAccessor.processReturnValue(innerAccessory, varClass);
			}
			socket(obj,innerAccessory);
			initObject(innerAccessory,null);
			Object old = ObjectAccessor.Accessible.variable(obj, varName, null);
			if(old==null) ObjectAccessor.Accessible.setVariable(obj, varName, innerControlPanel);
		}else{
			logger.error("Assembling "+obj.getClass().getName()+"."+varName+" error. module name is not specified");
		}
	}
	
	private void socketAccessories(HashMap<String,Object> accessoryPool,String toID,JIOPiConfig.Controlpanel assemblingConfig){
		if(assemblingConfig==null)return;
		//System.out.println("\nsocketAccessories to "+toID);
		Object toObj = accessoryPool.get(toID);
		if(toObj == null){
			logger.warn("socket to "+toID+" error,Object is not found at Module "+this.moduleVersion);
			return ;
		}
		
		List<Object[]> froms = assemblingConfig.sockets.get(toID);
		if(froms!=null){
			for(Object[] socketConfig:froms){
				String fromID = (String)socketConfig[0];
				Object[] args = (Object[])socketConfig[1];
				//System.out.println("socket from "+fromID +" to "+toID);
//				for(Object arg:args){
//					System.out.println("with arg "+arg);
//				}
				//get from accessory configuration
				HashMap<String,String> accessoryConfig = assemblingConfig.accessories.get(fromID);
				if(accessoryConfig == null){
					logger.error("socket from "+fromID+" to "+toID+"error,configuration not found at Module "+this.moduleVersion);
					continue;
				}
				String module = accessoryConfig.get("module");
				String controlpanel = accessoryConfig.get("controlpanel");
				String version = accessoryConfig.get("version");
				String configuration = accessoryConfig.get("configuration");
				String retrieve = accessoryConfig.get("retrieve");
				if(module==null || controlpanel==null){
					logger.error("socket from "+fromID+" to "+toID+"error,module or controlpanel must be set at Module "+this.moduleVersion);
					continue;
				}
				
				//4. instance module
				Object accessory = accessoryPool.get(fromID);
				if(accessory == null){
					//System.out.println("new accessory:+[id="+fromID+";module="+module+";version="+version+";controlpanel="+controlpanel+";configuration="+configuration+"]");
					accessory = newControlPanel(module, version,controlpanel,configuration, null);
					//System.out.println(" accessory socket a module : "+accessory.getClass());
					if(!"true".equals(retrieve)){
						accessoryPool.put(fromID, accessory);
						socketAccessories(accessoryPool,fromID,assemblingConfig);
					}
					initObject(accessory,null);
				}
				//double socket from accessory to toObj
				//System.out.println("===========socket double socketAccessories begin<<<");
				if(args!=null&&args.length>0)socket(accessory,toObj,args);
				socket(accessory,toObj);
				socket(toObj,accessory);
				//System.out.println("===========socket double socketAccessories over<<<");
			}
		}
	}
	
	/**
	 * call to.socket(from)
	 * @param from
	 * @param to
	 */
	private static void socket(Object from , Object to,Object... args){
		//1. find register interfaces
		Class<?> toClass = to.getClass();
		Class<?> fromClass = from.getClass();
		//System.out.println("------------>socket from "+from.getClass().getName()+" to >>>>. "+toClass.getName());
		ClassLoader toClassLoader = toClass.getClassLoader();
		if(toClassLoader!=null && toClassLoader instanceof JIOPiJarClassLoader){
			IBeanClassLoader moduleClassLoader = ((JIOPiJarClassLoader)toClassLoader).parentClassLoader;
			Class<?>[] registerBlueprintClasses = moduleClassLoader.getRegisterBlueprintClasses(toClass);
			if(registerBlueprintClasses!=null){
				HashSet<String> socketMethods = new HashSet<String>();
				//2. find socket method names
				//System.out.println("========= find socket method names "+registerBlueprintClasses.length);
				for(Class<?>registerBlueprintClasse:registerBlueprintClasses){
					//System.out.println("get sockets at "+registerBlueprintClasse.getName());
					ClassLoader blueprintClassLoader = registerBlueprintClasse.getClassLoader();
					if(blueprintClassLoader!=null && blueprintClassLoader instanceof JIOPiJarClassLoader){				
						BlueprintAnnotations.Interface blueprintConfig = ((JIOPiJarClassLoader)blueprintClassLoader).parentClassLoader.getBlueprintConfig(registerBlueprintClasse.getName());
						if(blueprintConfig!=null){
							//System.out.println("get sockets "+blueprintConfig.sockets.size());
							socketMethods.addAll(blueprintConfig.sockets);
						}
					}
				}
				
				//3. find proper method for socket and run it
				Method[] methods = toClass.getMethods();
//				System.out.println("++++++++++");
//				for(Method method:methods){
//					if("socket".equals(method.getName())){
//						System.out.print("have method "+method.getName()+" "+socketMethods.contains(method.getName()));
//						Class<?>[] parameterTypes = method.getParameterTypes();
//						System.out.print(" with parameterType "+parameterTypes[0].getName());
//						System.out.println();
//					}
//				}
//				System.out.println("++++++++++");
//				System.out.println("#################");
				for(Method method:methods){
					if(socketMethods.contains(method.getName())){
						//System.out.println(">>="+toClass+"==>>socket "+method.getName());
						Class<?>[] parameterTypes = method.getParameterTypes();
						int totalParameterNum = args.length+1;
						//System.out.println(">>fing method "+method.getName()+" with "+parameterTypes[0].getName());
						//System.out.println(parameterTypes.length+" == "+totalParameterNum+" and "+fromClass+" match "+parameterTypes[0]+" is "+ObjectAccessor.testMatch(fromClass, parameterTypes[0]));
						if(parameterTypes.length == totalParameterNum && ObjectAccessor.testMatch(fromClass, parameterTypes[0])>0){
							Object[] newArgs = new Object[totalParameterNum];
							newArgs[0]=from;
							for(int i=0;i<args.length;i++){
								newArgs[i+1]=args[i];
							}
							try {
								//System.out.println("!!!!!!===socket "+from +" to "+to);
								method.invoke(to, newArgs);
							} catch (Exception e) {
								e.printStackTrace();
								logger.error("socket from "+fromClass +" to "+toClass+" error",e);
							}
						}
					}
				}
				//System.out.println("#################");
			}
		}
	}
	
	private <T> T newControlPanel(String moduleName,String compatibleVersion,String registerClassName,String configurationID,Class<T> returnType,Object... args){
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		try{
			Thread.currentThread().setContextClassLoader(originContextClassLoader.get());
			return ccck.accessModuleConsole(moduleName, compatibleVersion).accessControlPanel(registerClassName,configurationID, returnType,false, args);
		}finally{
			Thread.currentThread().setContextClassLoader( contextClassLoader );
		}
	}
	
	public <T> T accessControlPanel(String registerName, Class<T> returnType, Object... args) {
		//System.out.println("accessControlPanel "+registerName);
		String[] registerNameInfo = parseRegisterName(registerName);
		return accessControlPanel(registerNameInfo[0],registerNameInfo[1],returnType,true,args);
	}
	
	private static void addAssemblingConfigFile(LinkedHashSet<URL> assemblingConfigFiles,URL file){
		if(file == null || assemblingConfigFiles == null)return;
		if(assemblingConfigFiles.contains(file)){
			assemblingConfigFiles.remove(file);
		}
		assemblingConfigFiles.add(file);
	}
	
	public JIOPiConfig.Controlpanel getAssemblingConfig(String registerClassName){
		String[] registerNameInfo = parseRegisterName(registerClassName);
		//System.out.println(registerNameInfo[1] == null);
		return getAssemblingConfig(registerNameInfo[0],registerNameInfo[1]);
	}
	
	private JIOPiConfig.Controlpanel getAssemblingConfig(String registerClassName,String configurationID){
		if("null".equals(configurationID)) throw new RuntimeException("test");
		//System.out.println("getAssemblingConfig "+registerClassName+" "+configurationID+(configurationID==null));
		//0.load Assembling config file : jiopi-config.xml, a cascading style config file
		// XMLMerger
		JIOPiConfig.Controlpanel cpConfigs = null;
		//find cpConfigs of this obj
		//System.out.println(moduleVersion+">>get assemblingConfigFile "+moduleClassLoader.getResource(JiopiConfigConstants.ASSEMBLING_FILE) );
		
		//XMLMerger cascading style ASSEMBLING_FILE
		LinkedHashSet<URL> assemblingConfigFiles = new LinkedHashSet<URL>();
		//1. load from context
		URL contextAssemblingConfigFile = originContextClassLoader.get().getResource(JiopiConfigConstants.ASSEMBLING_FILE);
		if(contextAssemblingConfigFile!=null)assemblingConfigFiles.add(contextAssemblingConfigFile);
		//2. load from originIBeanClassLoaderStack
		LinkedHashSet<IBeanClassLoader> callerModules = originIBeanClassLoaderStack.get();
		for(IBeanClassLoader icl:callerModules){
			URL assemblingConfigFile = icl.getLocalResource(JiopiConfigConstants.ASSEMBLING_FILE);
			addAssemblingConfigFile(assemblingConfigFiles,assemblingConfigFile);
		}
		//3. load from getAssemblingStack
		Stack<IBeanClassLoader> assemblingStack =  getAssemblingStack();
		for(IBeanClassLoader icl:assemblingStack){
			URL assemblingConfigFile = icl.getLocalResource(JiopiConfigConstants.ASSEMBLING_FILE);
			addAssemblingConfigFile(assemblingConfigFiles,assemblingConfigFile);
		}

		Document assemblingConfigDocument = null;
		if(assemblingConfigFiles.size()>0){
			URL[] assemblingConfigURls = assemblingConfigFiles.toArray(new URL[assemblingConfigFiles.size()]);
			for(int i=assemblingConfigURls.length-1;i>-1;i--){
				URL url = assemblingConfigURls[i];
				try{
					InputStream is = url.openStream();
					Document configDocument = XMLMerger.readDocumentAndCloseStream(is);
					if(assemblingConfigDocument==null){
						assemblingConfigDocument = configDocument;
					}else{
						assemblingConfigDocument = XMLMerger.mergeXML(assemblingConfigDocument, configDocument);
					}
				}catch(Exception e){
					logger.warn("load assemblingConfigDocument error "+url,e);
				}
				
			}
		}
		
		
		if(assemblingConfigDocument != null){
			JIOPiConfig assemblingConfig = new JIOPiConfig(assemblingConfigDocument);
			//find proper module configuration
			//System.out.println("module of this " + this.moduleVersion);
			JIOPiConfig.Module findModuleConfig = assemblingConfig.getModuleConfig(moduleVersion);
			if(findModuleConfig!=null){
				//System.out.println("findModuleConfig : "+findModuleConfig.id);
				//find proper control panel configuration
				cpConfigs = findModuleConfig.getMatchedControlpanel(moduleClassLoader, registerClassName,configurationID);
			}
		}
		return cpConfigs;
	}
	
	private <T> T accessControlPanel(String registerClassName,String configurationID, Class<T> returnType,boolean autoInit, Object... args) {
		boolean needClear = initOriginContextClassLoader();
		//System.out.println("accessControlPanel "+registerClassName+" "+configurationID+" "+(configurationID==null));
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		try {
			T instance = null;
			Thread.currentThread().setContextClassLoader(moduleClassLoader);
			getAssemblingStack().push(moduleClassLoader);
			
			//get assembling configuration of this registerClass
			//System.out.println(registerClassName+">>>getAssemblingConfig");
			JIOPiConfig.Controlpanel assemblingConfig = getAssemblingConfig(registerClassName,configurationID);
			//System.out.println(registerClassName+">>>getAssemblingConfig over "+assemblingConfig);
			//System.out.println("assemblingConfig "+registerClassName+" "+configurationID+" "+assemblingConfig);
			//replace args
			if(assemblingConfig!=null&&assemblingConfig.constructor.length>0&&(args==null || args.length<1)){//no args
				args = assemblingConfig.constructor;
			}
			Class<?> c = moduleClassLoader.loadModuleClass(registerClassName, true);
			//System.out.println("load "+c);
//			if("org.jiopi.ibean.example.blueprint.testblueprint.ClassControlPanel".equals(registerClassName)){
//				System.out.println("accessControlPanel test");
//				System.out.println(this.getClassLoader()+">"+c.getSuperclass().getName()+">"+c.getSuperclass().getClassLoader()+">"+((JIOPiJarClassLoader)c.getSuperclass().getClassLoader()).blueprintName);
//			}
			//System.out.println("loadModuleClass "+registerClassName+" = "+c.getName());
			//get instance type
			ModuleAnnotations.ControlPanel.Instantiation instantiation = moduleClassLoader.getInstantiationConfig(c.getName());
			//System.out.println(instantiation);
			if(instantiation!=null){
				String cacheName = null;
				if(InstanceType.SINGLETON.name().equals(instantiation.instancetype)){
					cacheName = c.getName();
				}
				else if(InstanceType.CONFIGURATION_SINGLETON.name().equals(instantiation.instancetype)){
					cacheName = mergeRegisterName(c.getName(),configurationID);
				}
				//System.out.println("cache name "+cacheName);
				if(cacheName!=null){
					instance = (T)objectPool.get(cacheName,returnType);
					if(instance == null){
						synchronized(c){
							instance = (T)objectPool.get(cacheName,returnType);
							if(instance == null){
								instance = instanceControlPanel(c,instantiation,returnType,assemblingConfig,autoInit,args);
								objectPool.put(cacheName, instance);
							}
						}
					}
				}else{
					instance = instanceControlPanel(c,instantiation,returnType,assemblingConfig,autoInit,args);
				}
			}
			else{
				instance = ObjectAccessor.constructor(c, returnType, args);
				autoAssembling(assemblingConfig,instance);
			}
			return instance;
		}catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}finally{
			Thread.currentThread().setContextClassLoader( contextClassLoader );
			if(needClear) clearOriginContextClassLoader();
			getAssemblingStack().pop();
		}
	}
	private <T> T instanceControlPanel(Class<?> c,ModuleAnnotations.ControlPanel.Instantiation instantiation, Class<T> returnType,JIOPiConfig.Controlpanel assemblingConfig,boolean autoInit, Object... args){
		T instance = null;
		if(instantiation.factoryMethod!=null&&instantiation.factoryMethod.length()>0){
			instance = ObjectAccessor.method(c, instantiation.factoryMethod, returnType, args);
		}else{
			instance = ObjectAccessor.constructor(c, returnType, args);
		}
		autoAssembling(assemblingConfig,instance);
		if(autoInit) initObject(instance,instantiation);
		return instance;
	}
	
	public <T> T accessControlPanelStrict(String registerName,Class<T> returnType, Object[] parameterTypes, Object... args) {
		String[] registerNameInfo = parseRegisterName(registerName);
		return accessControlPanelStrict(registerNameInfo[0],registerNameInfo[1],returnType,parameterTypes,true,args);
	}
	
	private <T> T accessControlPanelStrict(String registerClassName,String configurationID,Class<T> returnType, Object[] parameterTypes, boolean autoInit ,Object... args) {
		boolean needClear = initOriginContextClassLoader();
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		try {
			T instance = null;
			Thread.currentThread().setContextClassLoader(moduleClassLoader);
			getAssemblingStack().push(moduleClassLoader);
			
			//get assembling configuration of this registerClass
			JIOPiConfig.Controlpanel assemblingConfig = getAssemblingConfig(registerClassName,configurationID);
			//replace args
			if(assemblingConfig!=null&&assemblingConfig.constructor.length>0&&args==null || args.length<1){//no args
				args = assemblingConfig.constructor;
			}
			
			Class<?> c = moduleClassLoader.loadModuleClass(registerClassName, true);
			//get instance type
			ModuleAnnotations.ControlPanel.Instantiation instantiation = moduleClassLoader.getInstantiationConfig(c.getName());
			if(instantiation!=null){
				String cacheName = null;
				if(InstanceType.SINGLETON.name().equals(instantiation.instancetype)){
					cacheName = c.getName();
				}
				else if(InstanceType.CONFIGURATION_SINGLETON.name().equals(instantiation.instancetype)){
					cacheName = mergeRegisterName(c.getName(),configurationID);
				}
				if(cacheName!=null){
					instance = (T)objectPool.get(cacheName,returnType);
					if(instance == null){
						synchronized(c){
							instance = (T)objectPool.get(cacheName,returnType);
							if(instance == null){
								instance = instanceControlPanelStrict(c,instantiation,parameterTypes,returnType,assemblingConfig,autoInit,args);
								objectPool.put(cacheName, instance);
							}
						}
					}
				}else{
					instance = instanceControlPanelStrict(c,instantiation,parameterTypes,returnType,assemblingConfig,autoInit,args);
				}
			}
			else{
				instance = ObjectAccessor.strictConstructor(c,parameterTypes, returnType, args);
				autoAssembling(assemblingConfig,instance);
			}
			return instance;
		}catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}finally{
			Thread.currentThread().setContextClassLoader( contextClassLoader );
			if(needClear) clearOriginContextClassLoader();
			getAssemblingStack().pop();
		}
	}
	private <T> T instanceControlPanelStrict(Class<?> c,ModuleAnnotations.ControlPanel.Instantiation instantiation,Object[] parameterTypes, Class<T> returnType,JIOPiConfig.Controlpanel assemblingConfig,boolean autoInit,  Object... args){
		T instance = null;
		if(instantiation.factoryMethod!=null&&instantiation.factoryMethod.length()>0){
			instance = ObjectAccessor.strictMethod(c, instantiation.factoryMethod, returnType,parameterTypes ,args);
		}else{
			instance = ObjectAccessor.strictConstructor(c, parameterTypes, returnType, args);
		}
		
		autoAssembling(assemblingConfig,instance);
		if(autoInit) initObject(instance,instantiation);
		return instance;
	}
	
	private static void initObject(Object instance,ModuleAnnotations.ControlPanel.Instantiation instantiation){
		//1.get Annotation of given object
		if(instantiation==null){
			Class<?> instanceClass = instance.getClass();
			ClassLoader objClassLoader = instanceClass.getClassLoader();
			if(objClassLoader!=null && objClassLoader instanceof JIOPiJarClassLoader){
				instantiation = ((JIOPiJarClassLoader)objClassLoader).parentClassLoader.getInstantiationConfig(instanceClass.getName());
			}
		}
		if(instantiation!=null&&instantiation.initMethod!=null&&instantiation.initMethod.length()>0){
			if(instance instanceof ControlPanelImpl){
				ControlPanelImpl cpi = (ControlPanelImpl)instance;
				ObjectAccessor.method(cpi.wrapped, instantiation.initMethod, null);
			}else{
				ObjectAccessor.method(instance, instantiation.initMethod, null);
			}
		}
	}
	
	private final HashSet<String> initedClass = new HashSet<String>();
	public ControlPanelImpl accessStaticControlPanel(String registerName) {
		String[] registerNameInfo = parseRegisterName(registerName);
		boolean needClear = initOriginContextClassLoader();
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		try {
			Thread.currentThread().setContextClassLoader(moduleClassLoader);
			getAssemblingStack().push(moduleClassLoader);
			Class<?> c = moduleClassLoader.loadModuleClass(registerNameInfo[0], true);
			if(!initedClass.contains(c.getName())){
				JIOPiConfig.Controlpanel assemblingConfig = getAssemblingConfig(registerNameInfo[0],registerNameInfo[1]);
				if(assemblingConfig!=null) autoAssembling(assemblingConfig,c);
				initedClass.add(c.getName());
			}
			return new ControlPanelImpl(c);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}finally{
			Thread.currentThread().setContextClassLoader( contextClassLoader );
			if(needClear) clearOriginContextClassLoader();
			getAssemblingStack().pop();
		}
	}
	
	public IBeanClassLoader getClassLoader() {
		return moduleClassLoader;
	}

	public <T> T operate(String registerName, Class<T> returnType, Object... args) {
		String[] registerNameInfo = parseRegisterName(registerName);
		String registerClassName = registerNameInfo[0];
		String replaceName = moduleClassLoader.getRegisterValue(registerClassName);
		if(replaceName!=null) registerClassName = replaceName;
		String[] names = splitRegisterName(registerClassName);
		if(names == null) throw new IllegalArgumentException("registerName : "+registerName+" is not a definer of method");
		ControlPanelImpl cpi = accessStaticControlPanel(names[0]);
		if(ObjectAccessor.containsStaticMethod((Class<?>)cpi.wrapped, names[1], args)){
			return cpi.operate(names[1], returnType, args);
		}else{
			ControlPanel cp = accessControlPanel(mergeRegisterName(names[0],registerNameInfo[1]),ControlPanel.class);
			return cp.operate(names[1], returnType, args);
		}
	}
	
	public <T> T operateStrict(String registerName, Class<T> returnType,Object[] parameterTypes, Object... args) {
		String[] registerNameInfo = parseRegisterName(registerName);
		String registerClassName = registerNameInfo[0];
		String replaceName = moduleClassLoader.getRegisterValue(registerClassName);
		if(replaceName!=null) registerClassName = replaceName;
		String[] names = splitRegisterName(registerClassName);
		if(names == null) throw new IllegalArgumentException("registerName : "+registerName+" is not a definer of method");
		ControlPanelImpl cpi = accessStaticControlPanel(names[0]);
		if(ObjectAccessor.containsStaticMethod((Class<?>)cpi.wrapped, names[1], args)){
			return cpi.operateStrict(names[1], returnType, parameterTypes, args);
		}else{
			ControlPanel cp = accessControlPanel(mergeRegisterName(names[0],registerNameInfo[1]),ControlPanel.class);
			return cp.operateStrict(names[1], returnType, parameterTypes, args);
		}
	}
	
	public <T> T staticVariable(String registerName, Class<T> returnType) {
		String replaceName = moduleClassLoader.getRegisterValue(registerName);
		if(replaceName!=null) registerName = replaceName;
		String[] names = splitRegisterName(registerName);
		if(names == null) throw new IllegalArgumentException("registerName : "+registerName+" is not a definer of static variable");
		return accessStaticControlPanel(names[0]).variable(names[1], returnType);
	}
	
	/**
	 * 将注册名以最后一个点分为 类名 和 方法名/变量名 
	 * @param registerName
	 * @return 如果无法拆分，返回null
	 */
	private static String[] splitRegisterName(String registerName){
		if (registerName == null) return null;
		int pos = registerName.lastIndexOf('.');
		if(pos<0)return null;
		String[] split = new String[2];
		split[0]=registerName.substring(0,pos).trim();
		split[1]=registerName.substring(pos+1).trim();
		for(String s:split){
			if(s.length()==0) return null;
		}
		return split;
	}
	
	/**
	 * 将注册名 ? 后的 配置文件名解析出来
	 * @param registerName
	 * @return
	 */
	public static String[] parseRegisterName(String registerName){
		int pos = registerName.lastIndexOf('?');
		String[] split = new String[2];
		if(pos<0){
			split[0]=registerName;
		}else{
			split[0]=registerName.substring(0,pos).trim();
			split[1]=registerName.substring(pos+1).trim();
			if(split[1].length()==0)split[1]=null;
		}
		return split;
	}
	
	public static String mergeRegisterName(String name,String config){
		if(config!=null)name=name+"?"+config;
		return name;
	}

	public void refreshResource() {
		// TODO Auto-generated method stub
		
	}
	
	

}
